#include "gprs_uart_interface.h"
#include "platformAPI.h"
#include "esp_common.h"
#include "uart.h"

#define DEBUG_GPRS_UART 1
#undef DEBUG_GPRS_UART

#ifdef DEBUG_GPRS_UART
#include <stdio.h>
#endif

#define FAIL             (-1)
#define DATA_POOL_FULL   (-2)
#define DATA_POOL_EMPTY  (-3)


struct DataPool
{
    DATA_TYPE *buf;
    size_t size;
    size_t stock;
    size_t head;
    size_t tail;
};

typedef struct DataPool DataPool;

static DataPool *dataPool = NULL;

static void *(*pMalloc)(size_t sz) = malloc;
static void (*pFree)(void *ptr) = free;

static DataPool *createDataPool(size_t size)
{
    int ret;
    DataPool * obj = NULL;

    obj = (DataPool *) pMalloc(sizeof(DataPool));

#ifdef DEBUG_GPRS_UART
    printf("[createDataPool] :sizeof(DataPool) is %d\n",sizeof(DataPool));
    fflush(stdout);
#endif	

    if(!obj)
    {
#ifdef DEBUG_GPRS_UART
	printf("[createDataPool] :create pool failed\n");
	fflush(stdout);
#endif	
	return NULL;
    }

    obj->buf = (DATA_TYPE*) pMalloc(size * sizeof(DATA_TYPE));

    if(!(obj->buf))
    {
#ifdef DEBUG_GPRS_UART
	printf("[createDataPool] :get melloc failed\n");
	fflush(stdout);
#endif
	return NULL;
    }

    obj->size = size;
    obj->stock = 0;
    obj->head = 0;
    obj->tail = 0;
    
    return obj;
}

static int releaseDataPool(DataPool *obj)
{
    if(!obj || !(obj->buf))
    {
	return FAIL;
    }

    pFree(obj->buf);
    obj->buf = NULL;
    obj->size = 0;
    obj->stock = 0;
    obj->head = 0;
    obj->tail = 0;
    pFree(obj);
    obj = NULL;
    return 0;
}

static size_t updateRing(size_t len,size_t n)
{
    return ( (n + 1) == len ? 0 : (n + 1));
}

static int writeOneData(DataPool *obj, DATA_TYPE d)
{
    if( !obj || !(obj->buf))
    {
#ifdef DEBUG_GPRS_UART
	printf("[writeOneData] :error obj\n");
	fflush(stdout);
#endif
	return FAIL;
    }

    if( (obj->stock) < (obj->size) )
    {
	printf("%c",d);
	obj->buf[obj->head] = d;
	obj->head = updateRing(obj->size,obj->head);
	obj->stock++;
	return 0;
    }
    else
    {
	return DATA_POOL_FULL;
    }
}

static int readOneData(DataPool *obj ,DATA_TYPE * const pdata)
{
    size_t pos;

    if( !obj || !(obj->buf) )
    {
#ifdef DEBUG_GPRS_UART
	printf("[readOneData] :error obj\n");
	fflush(stdout);
#endif
	return FAIL;
    }

    if( obj->stock > 0)
    {
	pos = obj->tail;
	obj->tail = updateRing(obj->size,obj->tail);
	obj->stock--;
	*pdata = obj->buf[pos];
	return 0;
    }
    else
    {
	return DATA_POOL_EMPTY;
    }
}

static int writeToDataPool(DataPool * const obj, DATA_TYPE *buf, size_t len)
{
    size_t count = 0;
    int ret = 0;

    if( !obj || !(obj->buf))
    {
#ifdef DEBUG_GPRS_UART
	printf("[writeToDataPool] :error obj\n");
	fflush(stdout);
#endif
	return FAIL;
    }

#ifdef DEBUG_GPRS_UART
    printf("[writeToDataPool] : start:\n");
    printf("[writeToDataPool] : obj->size is %d\n",obj->size);
    printf("[writeToDataPool] : obj->stock is %d\n",obj->stock);
    printf("[writeToDataPool] : obj->head is %d\n",obj->head);
    printf("[writeToDataPool] : obj->tail is %d\n",obj->tail);
    fflush(stdout);
#endif

    for( count = 0; count < len; count++)
    {
	#ifdef DEBUG_GPRS_UART
	printf("[writeToDataPool] : count is %d\n",count);
	fflush(stdout);
	#endif
	ret = writeOneData(obj,buf[count]);
	if( ret != 0)
	{
	    len = count;
	    return ret;
	}
    }

#ifdef DEBUG_GPRS_UART
    printf("[writeToDataPool] : write done:\n");
    printf("[writeToDataPool] : obj->size is %d\n",obj->size);
    printf("[writeToDataPool] : obj->stock is %d\n",obj->stock);
    printf("[writeToDataPool] : obj->head is %d\n",obj->head);
    printf("[writeToDataPool] : obj->tail is %d\n",obj->tail);
    fflush(stdout);
#endif

    return 0;
}


static int readFromDataPool(DataPool * const obj, DATA_TYPE *buf, size_t len)
{
    size_t count = 0;
    int ret = 0;

    if (!obj || !(obj->buf))
    {
#ifdef DEBUG_GPRS_UART
	printf("[readFromDataPool] :error obj\n");
	fflush(stdout);
#endif
	return FAIL;
    }

#ifdef DEBUG_GPRS_UART
    printf("[readFromDataPool] : start：\n");
    printf("[readFromDataPool] : obj->size is %d\n",obj->size);
    printf("[readFromDataPool] : obj->stock is %d\n",obj->stock);
    printf("[readFromDataPool] : obj->head is %d\n",obj->head);
    printf("[readFromDataPool] : obj->tail is %d\n",obj->tail);
    fflush(stdout);
#endif

    for (count = 0; count < len; count++)
    {
	#ifdef DEBUG_GPRS_UART
	printf("[readFromDataPool] : count is %d\n",count);
	fflush(stdout);
	#endif
	ret = readOneData(obj, &buf[count]);
	if (ret != 0)
	{
	    break;
	}
    }

#ifdef DEBUG_GPRS_UART
    printf("[readFromDataPool] : read done:\n");
    printf("[readFromDataPool] : obj->size is %d\n",obj->size);
    printf("[readFromDataPool] : obj->stock is %d\n",obj->stock);
    printf("[readFromDataPool] : obj->head is %d\n",obj->head);
    printf("[readFromDataPool] : obj->tail is %d\n",obj->tail);
    fflush(stdout);
#endif
    return count;
}


static STATUS uart_tx_one_char(uint8 uart, uint8 TxChar)
{
    while (true) {
        uint32 fifo_cnt = READ_PERI_REG(UART_STATUS(uart)) & (UART_TXFIFO_CNT << UART_TXFIFO_CNT_S);

        if ((fifo_cnt >> UART_TXFIFO_CNT_S & UART_TXFIFO_CNT) < 126) {
            break;
        }
    }

    WRITE_PERI_REG(UART_FIFO(uart) , TxChar);
    return OK;
}

static void uart0_rx_intr_handler(void *para)
{
    /* uart0 and uart1 intr combine togther, when interrupt occur, see reg 0x3ff20020, bit2, bit0 represents
    * uart1 and uart0 respectively
    */
    uint8 RcvChar;
    uint8 uart_no = UART0;//UartDev.buff_uart_no;
    uint8 fifo_len = 0;
    uint8 buf_idx = 0;
    //uint8 fifo_tmp[128] = {0};

    uint32 uart_intr_status = READ_PERI_REG(UART_INT_ST(uart_no)) ;

    while (uart_intr_status != 0x0) {
        if (UART_FRM_ERR_INT_ST == (uart_intr_status & UART_FRM_ERR_INT_ST)) {
            //printf("FRM_ERR\r\n");
            WRITE_PERI_REG(UART_INT_CLR(uart_no), UART_FRM_ERR_INT_CLR);
        } else if (UART_RXFIFO_FULL_INT_ST == (uart_intr_status & UART_RXFIFO_FULL_INT_ST)) {
             fifo_len = (READ_PERI_REG(UART_STATUS(UART0)) >> UART_RXFIFO_CNT_S)&UART_RXFIFO_CNT;
            buf_idx = 0;
	    
            while (buf_idx < fifo_len) {
                //uart_tx_one_char(UART0, READ_PERI_REG(UART_FIFO(UART0)) & 0xFF);
		RcvChar = READ_PERI_REG(UART_FIFO(UART0)) & 0xFF;
                //uart_tx_one_char(UART0, RcvChar);
		writeOneData(dataPool,RcvChar);
                buf_idx++;
            }

            WRITE_PERI_REG(UART_INT_CLR(UART0), UART_RXFIFO_FULL_INT_CLR);
        } else if (UART_RXFIFO_TOUT_INT_ST == (uart_intr_status & UART_RXFIFO_TOUT_INT_ST)) {
             fifo_len = (READ_PERI_REG(UART_STATUS(UART0)) >> UART_RXFIFO_CNT_S)&UART_RXFIFO_CNT;
            buf_idx = 0;

            while (buf_idx < fifo_len) {
                //uart_tx_one_char(UART0, READ_PERI_REG(UART_FIFO(UART0)) & 0xFF);
		//printf("get one data\n");
		RcvChar = READ_PERI_REG(UART_FIFO(UART0)) & 0xFF;
                //uart_tx_one_char(UART0, RcvChar);
		writeOneData(dataPool,RcvChar);
                buf_idx++;
            }

            WRITE_PERI_REG(UART_INT_CLR(UART0), UART_RXFIFO_TOUT_INT_CLR);
        } else if (UART_TXFIFO_EMPTY_INT_ST == (uart_intr_status & UART_TXFIFO_EMPTY_INT_ST)) {
             WRITE_PERI_REG(UART_INT_CLR(uart_no), UART_TXFIFO_EMPTY_INT_CLR);
            CLEAR_PERI_REG_MASK(UART_INT_ENA(UART0), UART_TXFIFO_EMPTY_INT_ENA);
        } else {
            //skip
        }

        uart_intr_status = READ_PERI_REG(UART_INT_ST(uart_no)) ;
    }
}

void openGprsUart(int32_t boudrate)
{
    UART_WaitTxFifoEmpty(UART0);
    UART_WaitTxFifoEmpty(UART1);
    
    UART_ConfigTypeDef uart_config;

    uart_config.baud_rate    = BIT_RATE_115200;
    uart_config.data_bits    = UART_WordLength_8b;
    uart_config.parity       = USART_Parity_None;
    uart_config.stop_bits    = USART_StopBits_1;
    uart_config.flow_ctrl    = USART_HardwareFlowControl_None;
    uart_config.UART_RxFlowThresh = 100;
    uart_config.UART_InverseMask = UART_None_Inverse;
    UART_ParamConfig(UART0, &uart_config);
    uart_config.baud_rate    = BIT_RATE_74880;
    UART_ParamConfig(UART1, &uart_config);


    UART_IntrConfTypeDef uart_intr;
    uart_intr.UART_IntrEnMask = UART_RXFIFO_TOUT_INT_ENA | UART_FRM_ERR_INT_ENA | UART_RXFIFO_FULL_INT_ENA | UART_TXFIFO_EMPTY_INT_ENA;
    uart_intr.UART_RX_FifoFullIntrThresh = 10;
    uart_intr.UART_RX_TimeOutIntrThresh = 2;
    uart_intr.UART_TX_FifoEmptyIntrThresh = 20;
    UART_IntrConfig(UART0, &uart_intr);

    UART_SetPrintPort(UART1);
    UART_intr_handler_register(uart0_rx_intr_handler, NULL);
    ETS_UART_INTR_ENABLE();

    dataPool = (DataPool *)createDataPool(1024);
    if(dataPool == NULL)
    {
	printf("[%s] create dataPool fail\n",__FUNCTION__);
	fflush(stdout);
	systemDelayMs(300);
	system_restart();
    }
}

void closeGprsUart(void)
{
    releaseDataPool(dataPool);
}

int32_t gprsUartRead(uint8_t *buf,uint32_t size)
{
    int32_t ret = 0;
    ret = readFromDataPool(dataPool,buf,size);
    return ret;
}

int32_t gprsUartBlockRead(uint8_t *buf,uint32_t size,uint32_t ms)
{
    int32_t ret = 0;
    int32_t count = 0;
    uint8_t *p = NULL;

    p = (uint8_t *)pMalloc(size);

    if(p == NULL)
    {
	printf("[%s] fail to malloc tmp. size [%d]\n",__FUNCTION__,size);
	return -1;
    }
    
    do
    {
	memset(p,0,size);
	ret = readFromDataPool(dataPool,p,size);
	if(ret < 0)
	{
	    break;
	}
	else
	{
	    memcpy(buf+count, p, ret);
	    count += ret;
	    if(count >= size)
	    {
		break;
	    }
	}
	systemDelayMs(5);
	ms -= 5;
    } while(ms > 5);

    if(count > 0)
    {
	ret = count;
    }

    pFree(p);
    return ret;
}

int32_t gprsUartWrite(uint8_t *buf,uint32_t size)
{
    uint32_t i;
    for(i = 0; i < size; i++)
    {
        uart_tx_one_char(UART0, buf[i] & 0xFF);
    }
    return i;
}

void gprsCleanBuff(void)
{
    DATA_TYPE tmp[30];
    int32_t ret = 0;

    while(1)
    {
	ret = gprsUartRead(tmp,30);
	if(ret == 0)
	{
	    break;
	}
	systemDelayMs(30);
    }
}
